 //Alex Silkin

#include "KitRobotAgent.h"
#include "ScopedLock.h"
#include "KitRobotGUIInterface.h"
#include "GUIKitRobot.h"

#include <qpoint.h>

class QPoint;

using namespace std;

KitRobotAgent::KitRobotAgent(string name)
	: Agent(name)
{
	awakeThread = AwakeThread(this);
	awakeScheduled = false;

	scheduledMsgTableIsFree = false;

	partsRobotSem = Sema(0);
	animationSem = Sema(0);

	inspectorState = None;
	 //No need to allocate any more memory/pointers, since only using local objects for now
}

KitRobotAgent::~KitRobotAgent() {
	 //Nothing to delete yet
}

string KitRobotAgent::toString() const
{
	return "Kit Robot " + getName();
}


// ------------------------------------------------------------------------------------
// Messages:

void KitRobotAgent::msgCanIGoToTable() {											// Called by PartsAgent
	print("Kit Robot Agent: Parts Agent asked me if it can go to the table");
	scheduledMsgTableIsFree = true;
	stateChanged();
}

void KitRobotAgent::msgDoneDumpingParts() {										// Called by PartsAgent
	print("Kit Robot Agent: Parts Agent informed me if that it has dumped parts into the kit and I can bring parts to the table safely");
	partsRobotSem.post();
}

void KitRobotAgent::msgHereIsKit(Kit* k) {											// Called by ConveyourAgent
	print("Kit Robot Agent: on-conveyor informed me that it has a new empty kit for me");
	myKits.push_back(new MyKit(k, ReadyForTable));
	stateChanged();
}

void KitRobotAgent::msgKitFinished(Kit* k)  {										// Called by PartsAgent
	print("Kit Robot Agent: Parts Agent informed me that it has finished a kit");
   myKits.push_back(new MyKit(k, ReadyForInspection));
   stateChanged();
}

void KitRobotAgent::msgHereIsKitState(InspectorKitState state) {			// Called InspectorKitState
	print("Kit Robot Agent: inspector has informed me that the kit state is " + state);
	this->inspectorState = state;
    stateChanged();
}

void KitRobotAgent::msgAnimationDone() {											// Called by GUI
	print("Kit Robot Agent: my GUI has informed me that it has finished animating current action");

	animationSem.post();

	/*if(animationSem.getValue() < 1)
		animationSem.post();
	else
		print("ERROR: SOMEONE (PROBABLY KIT ROBOT GUI) HAS CALLED MSGANIMATIONDONE MORE THAN ONE TIME");
*/	
}

 //------------------------------------------------------------------------------------


// ------------------------------------------------------------------------------------
// Scheduler:
bool KitRobotAgent::pickAndExecuteAnAction() {

	if(scheduledMsgTableIsFree) {
		tellPartsAgentTableIsFree();
		return true;
	}

	if(inspectorState != None) {
		takeKitToDeliveryStation();
		return true;
	}

	MyKit* kit = NULL;
	

	kit = findKitByStatus(ReadyForInspection);
	if(kit != NULL) {
		if(table->isInspectorSlotAvailable()) {
			takeKitToInspection(kit);
			return true;
		} else {
			scheduleAwake();
		}
	}

	kit = findKitByStatus(ReadyForTable);
	if(kit != NULL) {
		if(table->isATableSlotAvailable()) {
			takeKitOffConveyorAndPlaceOnKitStand(kit);
			return true;
		} else {
			scheduleAwake();
		}
	}

	return false;

}    

 //------------------------------------------------------------------------------------

// Non-norms
void KitRobotAgent::handleBreak() {
	((GUIKitRobot*) gui)->DoBreak();
}
void KitRobotAgent::handleFix() {
	((GUIKitRobot*) gui)->DoFix();
}
// ------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------
//Actions

void KitRobotAgent::takeKitOffConveyorAndPlaceOnKitStand(MyKit* myKit) {
	print("Kit Robot: starting to take kit off conveyor and place on kit stand");
	myKit->status = NoAction;
	   
	int tableIndex = table->getAvailableFirstTwoTables();
	   
	string output = "Kit Robot: deciding to drop the kit on table slot " + tableIndex;

	print(output);

	if(tableIndex == -1) {
	    return;
	}

	gui->DoGoToDestination(onConveyor->getPickUpKitPosition());       
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	gui->DoPickUpKit(myKit->kit);
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	onConveyor->msgKitPickedUp();										// This is not in the design but SHOULD BE IMPLEMENTED!!!

	gui->DoGoToDestination(table->getTableSlotPosition(tableIndex));       
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	gui->DoDropOffKit();   
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	table->insert(myKit->kit, tableIndex);
	myKits.remove(myKit);

	gui->DoGoToNeutralPosition();   
	animationSem.wait();     // Sleep until msgAnimationDone() is called
			
	partsRobot->msgMakeKit(myKit->kit);

	stateChanged();
}
   
void KitRobotAgent::takeKitToInspection(MyKit* myKit) {
	myKit->status = NoAction;

	int tableIndex = table->getIndexOf(myKit->kit);

	print("Kit Robot: taking kit to the inspector slot from slot " + tableIndex);

	gui->DoGoToDestination(table->getTableSlotPosition(tableIndex));       
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	gui->DoPickUpKit(myKit->kit);
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	table->remove(tableIndex);

	gui->DoGoToDestination(table->getTableSlotPosition(2));       
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	gui->DoDropOffKit();   
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	table->insert(myKit->kit, 2);
	
	gui->DoGoToNeutralPosition();   
	animationSem.wait();     // Sleep until msgAnimationDone() is called
	   
	inspector->msgInspectKit();     

	myKits.remove(myKit);
	stateChanged();
}
   
void KitRobotAgent::takeKitToDeliveryStation() {
	print("Kit Robot: taking kit to the off-conveyor");

	Kit* kit = table->peek(2);

	gui->DoGoToDestination(table->getTableSlotPosition(2));       
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	gui->DoPickUpKit(kit);
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	table->remove(2);
	
	if(inspectorState == Good)
		gui->DoGoToDestination(offGoodConveyor->getDropOffKitPosition()); 
	else
		gui->DoGoToDestination(offBadConveyor->getDropOffKitPosition());      
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	gui->DoDropOffKit();   
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	gui->DoGoToNeutralPosition();   
	animationSem.wait();     // Sleep until msgAnimationDone() is called

	if(inspectorState == Good)
		offGoodConveyor->msgHereIsFinishedKit(kit);
	else
		offBadConveyor->msgHereIsFinishedKit(kit);

	inspectorState = None;
}

void KitRobotAgent::tellPartsAgentTableIsFree() {
	print("Kit Robot: telling parts robot that it can go to the table safely");
	scheduledMsgTableIsFree = false;
	this->partsRobot->msgYouCanGoToTable();
	partsRobotSem.wait();
	//Wake up when parts agents says that table is free;
}

// ------------------------------------------------------------------------------------


 //------------------------------------------------------------------------------------
//Utility Method(s)

KitRobotAgent::MyKit* KitRobotAgent::findKitByStatus(KitStatus status)
{
	{
		ScopedLock scopedKitListLock (&kitsListLock);
		for (std::list<MyKit*>::iterator it = myKits.begin(); it != myKits.end(); it++)
		{
			if ( (*it)->status == status)
			{
				return (*it);
			}
		}
	}
	return NULL;
}

void KitRobotAgent::scheduleAwake() {
	if(!awakeScheduled) {
		print("Kit Robot: scheduling awaking to see later if a table slot becomes available");

		awakeScheduled = true;
		awakeThread.start();
	}
}

void KitRobotAgent::completeScheduledAwake() {
	print("Kit Robot: waking up to see if there is a table slot available");

	awakeScheduled = false;
	stateChanged();
}

// ------------------------------------------------------------------------------------